home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_100 / 130_01 / bdoscom.c < prev    next >
Text File  |  1985-03-09  |  8KB  |  256 lines

  1.                   Doing Sneaky Things with CP/M
  2.  
  3.  
  4.     When CP/M version 1.4 was all the rage, we didn't have
  5. too many problems with incompatibility between disk formats.
  6. Almost everyone used 8inch single density IBM formatted disks
  7. and the standard skewing factor built in to the CP/M bdos.
  8. People who wrote those clever little utilities to manipulate
  9. the directory or copy system tracks and needed access to the
  10. bios routines, could do so in the knowledge that they were
  11. dealing with a standard disk configuration.
  12.  
  13.     CP/M version 2.2 changed all that.  By putting all of
  14. the hardware dependencies in the bios and specifying standard
  15. routines for accessing these, the operating system could be
  16. configured for a wide variety of disk formats from the humble
  17. mini-floppy to the giant hard disk.  Unfortunately, many of
  18. the utilities haven't caught up with the change.  I met a very
  19. sad man the other day who had backed up an important disk
  20. using COPY.COM - a track to track copier which has been around
  21. for a long time.  Unfortunately, he did it on a double-density
  22. system and is still trying to unscramble the result.
  23.  
  24.     One way of handling the problem is to #define all the
  25. important parameters in your programs to produce versions
  26. specially tailored for different formats.  This is not too
  27. difficult - STAT DSK: will give you most of the numbers - but
  28. it does cause problems if you have a number of different
  29. formats.  The Altos multi-user system on which I sometimes
  30. work, for example, has a hard disk and both single and
  31. double density floppies.  Trying to keep track of three
  32. different versions of a number of utilities is more than I
  33. want to do.
  34.  
  35.     A better way is to use CP/M itself.  After all,
  36. the information is in the bios and Digital Research have
  37. specified the way to get to it.  The Version 2.0 alteration
  38. guide has all the data but, in the manner of most Digital
  39. Research publications, the explanations are less than lucid.
  40. To make life easier, the collection of functions below are
  41. what I have been using for some time.  They have been tested
  42. with both versions of CP/M and on a variety of disk formats
  43. and have (in their present form) not caused any major
  44. disaster of which I am aware.  Caveat emptor, though - If
  45. you use these to write a disk hacking program, make sure
  46. you test it on an unimportant disk first!
  47.  
  48.     The functions are split into two groups.  The first
  49. accesses the bdos in the approved manner and are pretty
  50. straightforward.  Getparams() determines which version
  51. of CP/M it is running under and then sets the external
  52. parameter veriables accordingly.  For version 1.4, it
  53. uses 'canned' values appropriate to 8inch disks - these
  54. will need altering for special 1.4 versions modified
  55. for mini-floppies or other formats.  Note that the
  56. parameter values are set for the current CP/M default
  57. drive.  If your program is to work in an environment
  58. where there are mixed types, you must select each
  59. prior to calling the function. 
  60.  
  61.     The second group uses the bios low-level routines
  62. and can easily get you into trouble if you don't know what
  63. you are doing. I will assume that you do.  Sectran() is
  64. the critical routine and performs translation between the
  65. logical and physical sector numbers to take account of the
  66. skew factor.  Note again that canned values are used for
  67. CP/M version 1.4.
  68.  
  69.     Before sectran() is called, the disk on which you
  70. are going to operate must be selected using seldsk().  It
  71. is not good enough to use the currently selected drive as
  72. seldsk() is the only way to access the address of the 
  73. sector translation table in the bios and initialize the
  74. value of transtable which sectran uses.  All this is a bit
  75. messy, but then you weren't supposed to do this sort of thing
  76. with CP/M, were you?
  77.  
  78.                         Nick Hammond
  79.  
  80.  
  81. /********* BDOSCOM - Routines for accessing the BDOS *********/
  82.  
  83. /*    the external variables below are set by getparams()     */
  84. /*    to the appropriate value for the current default        */
  85. /*    drive. They may then be accessed by the host program    */ 
  86.  
  87. int    offset,        /* no of reserved system tracks */
  88.     secpertrk,    /* 128 byte records per track */
  89.     entries,    /* no of 32 byte directory entries */
  90.     blocksize,    /* size of a disk block in bytes */
  91.     capacity;    /* disk capacity in blocks */
  92.  
  93.  
  94. getparams()
  95. {    /* set parameters for current default drive */
  96.     struct dpb{        /* disk parameter block */
  97.         int    spt;
  98.         char    bsh,
  99.             blm,
  100.             exm;
  101.         int    dsm,
  102.             drm,
  103.             all,
  104.             cks,
  105.             off;
  106.         }
  107.             *bp;
  108.     if (cpmvsn() == 1){    /* set for std 8in disk */
  109.         secpertrk = 26;
  110.         blocksize = 1024;
  111.         capacity = 243;
  112.         entries = 64;
  113.         offset = 2;
  114.         }
  115.     else{            /* get data from dpb */
  116.         bp = dpbaddr();
  117.         secpertrk = bp -> spt;
  118.         blocksize = 1024 << ((bp -> bsh) - 3);
  119.         capacity = (bp -> dsm) + 1;
  120.         entries = (bp -> drm) + 1;
  121.         offset = bp -> off;
  122.         }        
  123. }
  124.  
  125. bdos16(funct,param)
  126. int    funct,param;
  127. {    /* perform bdos call and return value of HL */
  128.     /* note: bdos library function returns value of A */
  129.     return call(0005,0,0,funct,param);
  130. }
  131. cpmvsn()
  132. {    /* return CP/M version no */
  133.     return (bdos16(12,0) ? 2 : 1);
  134. }
  135.  
  136. seldrive(drive)
  137. int    drive;
  138. {    /* select drive as current default */
  139.     bdos(14,drive);
  140. }
  141.  
  142.  
  143. defdrive()
  144. {    /* return no of current default disk */
  145.     return bdos(25,0);
  146. }
  147.  
  148. dpbaddr()
  149. {    /* return address of dpb of current default drive */
  150.     return bdos16(31,0);
  151. }
  152.  
  153.  
  154. /********** BIOSCOM - Low Level CP/M Communications **********/
  155.  
  156. /*    transtable is a pointer to the logical/physical     */
  157. /*    sector translation table for the currently selected */
  158. /*    disk drive.  It is used by sectran() in CP/M V2.2   */ 
  159.  
  160. char    *transtable;    /* ^ sector translation table */
  161.  
  162. bios16(offset,param)
  163. int    offset,        /* ordinal number in bios vector */
  164.     param;        /* parameter for bios call */
  165. {    /* perform bios call and return the value of HL */
  166.     /* note: bios library function returns value of A */
  167.     int    *wbp;
  168.     char    *addr;
  169.     wbp = 0x0001;
  170.     addr = *wbp + (--offset) * 3;
  171.     return call(addr,0,0,param,0);
  172. }
  173.  
  174. home()
  175. {    /* disk to trk 0 */
  176.     bios(8);
  177. }
  178.  
  179. seldsk(disk)
  180. char    disk;
  181. {    /* select disk and initialize transtable */
  182.     struct dph{        /* disk parameter header */
  183.         char    *xlt;
  184.         int    scratch[3];
  185.         char    *dirbuf;
  186.         struct
  187.            dpb    *dpbptr;
  188.         char    *csv,
  189.             *alv;
  190.         }
  191.             *hp;
  192.     hp = bios16(9,disk);
  193.     transtable = hp -> xlt;
  194. }
  195.  
  196. settrk(trk)
  197. int    trk;
  198. {    /* set track */
  199.     bios(10,trk);
  200.     return trk;
  201. }
  202.  
  203. setsec(sec)
  204. int    sec;
  205. {    /* set sector */
  206.     bios(11,sec);
  207.     return sec;
  208. }
  209.  
  210. setdma(dma)
  211. char    *dma;
  212. {    /* set dma transfer address */
  213.     bios(12,dma);
  214.     return dma;
  215. }
  216.  
  217. readsec()
  218. {    /* read sector */
  219.     return bios(13,0;
  220. }
  221.  
  222. writesec()
  223. {    /* write sector */
  224.     return bios(14,0);
  225. }
  226.  
  227. writedsec()
  228. {    /* write a directory sector */
  229.     return bios(14,1);
  230. }
  231.  
  232. sectran(lsec,vsn)
  233. int    lsec,        /* logical sector number */
  234.     vsn;        /* CP/M bios version */
  235. /* note that this routine must be called with the version #
  236.    as a parameter.  It is not good practice to obtain the
  237.    version by a bdos call while in a bios level routine
  238.    since there is no guarantee that the bdos has not been
  239.    overlayed.  The version should be obtained by the calling
  240.    program by a previous call to cpmvsn() */
  241. {    /* logical to physical sector translation */
  242.     int    incr;
  243.     int    *wbp;
  244.     char    *addr;
  245.     if (vsn == 1){    /* use standard skew factor */
  246.         incr = lsec >= 13 ? 2 : 1;
  247.         return ((lsec * 6) + incr) % 26;
  248.         }
  249.     else{
  250.         if (transtable == 0) return lsec;
  251.         wbp = 0x0001;
  252.         addr = *wbp + 45;
  253.         return call(addr,0,0,lsec,transtable) & 0xFF;
  254.         }
  255. }
  256.